package com.hjm.friend.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.hjm.common.core.constants.CacheConstants;
import com.hjm.common.core.constants.Constants;
import com.hjm.common.core.enums.ExamType;
import com.hjm.common.core.enums.ResultCode;
import com.hjm.friend.domain.exam.Exam;
import com.hjm.friend.domain.exam.ExamQuestion;
import com.hjm.friend.domain.exam.dto.ExamQueryDTO;
import com.hjm.friend.domain.exam.dto.ExamRankQueryDTO;
import com.hjm.friend.domain.exam.vo.ExamRankVO;
import com.hjm.friend.domain.exam.vo.ExamVO;
import com.hjm.friend.domain.user.UserExam;
import com.hjm.friend.mapper.exam.ExamMapper;
import com.hjm.friend.mapper.exam.ExamQuestionMapper;
import com.hjm.friend.mapper.user.UserExamMapper;
import com.hjm.redis.service.RedisService;
import com.hjm.security.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class ExamCacheManager {

    @Autowired
    private RedisService redisService;

    @Autowired
    private ExamMapper examMapper;


    @Autowired
    ExamQuestionMapper examQuestionMapper;

    @Autowired
    private  UserExamMapper userExamMapper;


    // 获取 exam 的长度
    public Long getListSize(Integer examListType,Long userId){
        String examListKey = getExamListKey(examListType,userId);
        return redisService.getListSize(examListKey);
    }

    public Long getExamQuestionListSize(Long examId) {
        String examQuestionListKey = getExamQuestionListKey(examId);
        return redisService.getListSize(examQuestionListKey);
    }

    public Long getRankListSize(Long examId) {
        String rankListKey = getRankListKey(examId);
        return redisService.getListSize(rankListKey);
    }


    // 根据条件获取 列表
    public List<ExamVO> getExamVOList(ExamQueryDTO examQueryDTO,Long userId){
        int start = (examQueryDTO.getPageNum() -1) * examQueryDTO.getPageSize();    // 开始下标
        int end = start + examQueryDTO.getPageSize() -1 ;                           // 结束下标
        String examListKey = getExamListKey(examQueryDTO.getType(),userId);
        List<Long> examIdList = redisService.getCacheListByRange(examListKey, start, end, Long.class);
        List<ExamVO> examVOList = assembleExamVOList(examIdList);
        if(CollectionUtil.isEmpty(examVOList)){
            // 没有找到数据，说明 redis 中数据 存在问题，需要刷新以下缓存
            examVOList = getExamListByDB(examQueryDTO,userId);
            refreshCache(examQueryDTO.getType(),userId);
        }
        return examVOList;

    }

    public List<ExamRankVO> getExamRankList(ExamRankQueryDTO examRankQueryDTO) {
        int start = (examRankQueryDTO.getPageNum() -1) * examRankQueryDTO.getPageSize();    // 开始下标
        int end = start + examRankQueryDTO.getPageSize() -1 ;                           // 结束下标
        String examRankListKey = getRankListKey(examRankQueryDTO.getExamId());
        return redisService.getCacheListByRange(examRankListKey, start, end, ExamRankVO.class);
    }

    // 从数据库中获取信息
    public List<ExamVO> getExamListByDB(ExamQueryDTO examQueryDTO,Long userId) {
        PageHelper.startPage(examQueryDTO.getPageNum(),
                examQueryDTO.getPageSize());
        if(examQueryDTO.getType().equals(ExamType.USER_EXAM_LIST)){
            // 查询我的竞赛
            return userExamMapper.selectUserExamList(userId);
        }else{

            return examMapper.selectExamList(examQueryDTO);
        }

    }

    public List<Long> getAllUserExamList(Long userId) {
        // 获取用户竞赛的 key
        String userExamListKey= getExamListKey(ExamType.USER_EXAM_LIST.getType(),userId);
        List<Long> userExamList = redisService.getCacheListByRange(userExamListKey, 0, -1, Long.class);
        if(CollectionUtil.isNotEmpty(userExamList)){
            return userExamList;
        }

        List<UserExam> userExams = userExamMapper.selectList(new LambdaQueryWrapper<UserExam>().
                eq(UserExam::getUserId, userId));
        if(CollectionUtil.isEmpty(userExams)){
            return null;
        }
        refreshCache(ExamType.USER_EXAM_LIST.getType(),userId);

        return userExams.stream().map(UserExam::getExamId).collect(Collectors.toList());
    }

    public void refreshCache(Integer examListType,Long userId) {
        List<Exam> examList =new ArrayList<>();
        // 从数据库中获取 符合条件的 exam 放入 examList 中
        if(ExamType.EXAM_NOT_STARTED_LIST.getType().equals(examListType)){
            // 未开始竞赛进行刷新
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().
                            select(Exam::getExamId,Exam::getStartTime,Exam::getEndTime,Exam::getTitle).
                            gt(Exam::getEndTime, LocalDateTime.now()).
                            eq(Exam::getStatus, Constants.TRUE).
                            orderByDesc(Exam::getCreateTime));

        }else if(ExamType.EXAM_HISTORY_LIST.getType().equals(examListType)){
            // 历史竞赛
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>().
                    select(Exam::getExamId,Exam::getStartTime,Exam::getEndTime,Exam::getTitle).
                    lt(Exam::getEndTime, LocalDateTime.now()).
                    eq(Exam::getStatus, Constants.TRUE).
                    orderByDesc(Exam::getCreateTime));
        } else if (ExamType.USER_EXAM_LIST.getType().equals(examListType)) {
            // 我的竞赛
            List<ExamVO> examVOList = userExamMapper.selectUserExamList(userId);
            examList = BeanUtil.copyToList(examVOList,Exam.class);
        }
        if(CollectionUtil.isEmpty(examList)){
            return ;
        }

        // examList 是从数据库中获取的数据，现在要同步到 redis 中去
        Map<String ,Exam> examMap =new HashMap<>();
        List<Long> examIdList =new ArrayList<>();
        for(Exam exam : examList){
            examMap.put(getDetailKey(exam.getExamId()),exam);   // 存竞赛信息
            examIdList.add(exam.getExamId());
        }
        redisService.multiSet(examMap);

        redisService.deleteObject(getExamListKey(examListType,userId));                   // 删除之前存储的数据
        redisService.rightPushAll(getExamListKey(examListType,userId),examIdList);        // 存储 比较全的数据
    }

    // 刷新 竞赛题目id 列表
    public void refreshExamQuestionList(Long examId) {
        // 从数据库中获取 该竞赛的 题目 列表
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>().
                eq(ExamQuestion::getExamId, examId).
                orderByAsc(ExamQuestion::getQuestionOrder));

        // 转换为 题目id 列表
        List<Long> eqListId = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        // 存入 redis 中
        redisService.deleteObject(getExamQuestionListKey(examId));
        redisService.rightPushAll(getExamQuestionListKey(examId),eqListId);
    }

    public void refreshRankCache(Long examId) {
        // 从数据库中获取 该竞赛的 排名 列表
        List<ExamRankVO> rankList = userExamMapper.selectExamRankList(examId);
        if(CollectionUtil.isEmpty(rankList)){
            return ;
        }
        String rankListKey = getRankListKey(examId);
        // 存入 redis 中
        redisService.deleteObject(rankListKey);
        redisService.rightPushAll(getRankListKey(examId),rankList);
    }


    public List<ExamVO> assembleExamVOList(List<Long> examIdList) {
        if(CollectionUtil.isEmpty(examIdList)){
            // redis 中 无数据
            return null;
        }
        List<String> detailKeyList = new ArrayList<>();
        for(Long examId : examIdList){
            detailKeyList.add(getDetailKey(examId));
        }
        List<ExamVO> examVOList = redisService.multiGet(detailKeyList, ExamVO.class);
        CollUtil.removeNull(examVOList);
        if(CollectionUtil.isEmpty(examVOList) || examVOList.size()  != examIdList.size()){
            // redis 中 数据 存在问题
            return null;
        }
        return examVOList;
    }



    public String getFirstQuestion(Long examId) {
        return redisService.indexForListValue(getExamQuestionListKey(examId),0,Long.class).toString();
    }


    public String preQuestion(Long examId, Long questionId) {
        // 从 题目列表中 获取 下标
        Long index = redisService.indexOfForListValue(getExamQuestionListKey(examId), questionId);
        if(index == 0){
            throw new ServiceException(ResultCode.FAILED_FIRST_QUESTION);
        }
        return redisService.indexForListValue(getExamQuestionListKey(examId),index-1,Long.class).toString();
    }

    public String nextQuestion(Long examId, Long questionId) {
        Long listSize = getExamQuestionListSize(examId);
        // 从 题目列表中 获取 下标
        Long index = redisService.indexOfForListValue(getExamQuestionListKey(examId), questionId);
        if(index == listSize-1 ){
            throw new ServiceException(ResultCode.FAILED_LAST_QUESTION);
        }
        return redisService.indexForListValue(getExamQuestionListKey(examId),index+1,Long.class).toString();
    }


    // 用户竞赛的关系 缓存
    public void addUserExamCache(Long userId,Long examId){
        String userExamKey = getExamListKey(ExamType.USER_EXAM_LIST.getType(), userId);
        redisService.leftPushForList(userExamKey,examId);
    }

    // 竞赛 类别
    public  String getExamListKey(Integer examListType,Long userId) {
        if(examListType.equals(ExamType.EXAM_NOT_STARTED_LIST.getType())){
            return CacheConstants.EXAM_NOT_STARTED_LIST;
        } else if (examListType.equals(ExamType.EXAM_HISTORY_LIST.getType())) {
            return CacheConstants.EXAM_HISTORY_LIST;
        }else{
            return CacheConstants.USER_EXAM_LIST+userId;
        }
    }


    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL+examId;
    }

    private String getExamQuestionListKey(Long examId) {
        return CacheConstants.EXAM_QUESTION_LIST + examId;
    }

    private String getRankListKey(Long examId) {
        return CacheConstants.EXAM_RANK_LIST + examId;
    }



}

